package com.panopset.gui;

import static com.panopset.Util.log;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.Collections;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.Vector;
import java.util.Map.Entry;

import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;

import com.panopset.Strings;
import com.panopset.UtilIO;
import com.panopset.centralen.resource.Favicon;
import com.panopset.swing.App;
import com.panopset.swing.Document;
import com.panopset.swing.FieldGui;
import com.panopset.swing.Scaffolding;

/**
 * 
 * Edit property values.
 * 
 * @author Karl Dinwiddie
 * 
 */
public abstract class PropvalEditor extends Document {

	public PropvalEditor(final String propertiesFileName) {
		this(null, propertiesFileName);
	}

	/**
	 * @param defaultResourcePath
	 *            Default resource path.
	 * @param propertiesFileName
	 *            Name of properties file, relative to the application base
	 *            directory.
	 */
	public PropvalEditor(final String defaultResourcePath,
			final String propertiesFileName) {
		super(defaultResourcePath, propertiesFileName);
		setFieldGui(new FieldGui() {

			@Override
			public String getValue() {
				StringWriter sw = new StringWriter();
				for (Entry<PVEID, ValidatingDataEntry> entry : getOrderedEntries()
						.entrySet()) {
					sw.append(entry.getKey().toString());
					sw.append(UtilIO.MAP_SEP);
					sw.append(entry.getValue().getValue());
					sw.append(Strings.getEol());
				}
				return sw.toString();
			}

			@Override
			public void setValue(final String value) {
				getPropsPanel().removeAll();
				GridBagConstraints gbc1 = new GridBagConstraints();
				GridBagConstraints gbc2 = new GridBagConstraints();
				gbc1.gridx = 1;
				gbc1.gridy = 0;
				gbc1.anchor = GridBagConstraints.EAST;
				gbc2.gridx = 2;
				gbc2.gridy = 0;
				gbc2.weightx = 1.0;
				gbc2.fill = GridBagConstraints.HORIZONTAL;

				StringReader sr = new StringReader(value);
				BufferedReader br = new BufferedReader(sr);
				if (keysplr != null) {
					for (PVEID nv : keysplr.getKeys()) {
						getDataEntry(nv);
					}
				}
				try {
					String s = br.readLine();
					while (s != null) {
						StringTokenizer st = new StringTokenizer(s,
								UtilIO.MAP_SEP);
						String key = "";
						String val = "";
						if (st.hasMoreElements()) {
							key = st.nextToken();
						}
						if (st.hasMoreElements()) {
							val = st.nextToken();
						}
						getDataEntry(new PVEID(key)).setValue(val);
						s = br.readLine();
					}
				} catch (IOException ex) {
					log(ex);
				}
				for (Entry<PVEID, ValidatingDataEntry> e : getOrderedEntries()
						.entrySet()) {
					ValidatingDataEntry de = e.getValue();
					getPropsPanel().add(
							new JLabel("   " + e.getKey().getLabel()), gbc1);
					getPropsPanel().add(de.getTitledPanel().getPanel(), gbc2);
					gbc1.gridy++;
					gbc2.gridy++;
					de.updateGui();
				}
				getPropsPanel().invalidate();
				UtilGUI.updateComponent(getPropsPanel());
			}
		});
	}

	/**
	 * Number of test keys is 2.
	 */
	private static final int NUM_KEYS = 2;
	/**
	 * 100.
	 */
	private static final int ABOUT_SIZE = 100;

	/**
	 * Test PropvalEditor.
	 * 
	 * @param s
	 *            Not used.
	 */
	public static void main(final String... s) throws Exception {
		Scaffolding.s.setHome(new File(UtilIO.combinePaths(UtilIO.getHome(),
				"Panopset")));
		new App(new Favicon(Favicon.PANOPSET_ICON_PATH)) {
			@Override
			public String getApplicationName() {
				return "PropvalEditorTest";
			}

			@Override
			public Component createMainPanel() {
				PropvalEditor pve = new PropvalEditorInteger(null, "foo.txt");
				pve.setKeySupplier(new KeySupplier() {

					@Override
					public Vector<PVEID> getKeys() {
						Vector<PVEID> s = new Vector<PVEID>();
						for (int i = 0; i < NUM_KEYS; i++) {
							StringBuffer lbl = new StringBuffer();
							lbl.append("y" + i);
							s.add(new PVEID(lbl.toString(), "k" + i));
						}
						return s;
					}
				});
				return pve.getPanel();
			}

			@Override
			protected PopupBox getAboutBox() {
				return new PopupBox(new JPanel(), new Dimension(ABOUT_SIZE,
						ABOUT_SIZE), App.app.f.getFrame()) {
				};
			}
		}.show();
	}

	public Map<PVEID, ValidatingDataEntry> getEntries() {
		getValue();
		return getOrderedEntries();
	}

	private Map<PVEID, ValidatingDataEntry> entries;

	private Map<PVEID, ValidatingDataEntry> getOrderedEntries() {
		if (entries == null) {
			entries = Collections
					.synchronizedSortedMap(new TreeMap<PVEID, ValidatingDataEntry>());
		}
		return entries;
	}

	public void addKey(final String name) {
		PVEID key = new PVEID(name);
		getOrderedEntries().put(key, new ValidatingDataEntry(key));
	}

	/**
	 * Get the data entry object for a given named key. It will be created if it
	 * does not already exist.
	 * 
	 * @param key
	 *            Key.
	 * @return Data entry.
	 */
	private ValidatingDataEntry getDataEntry(final PVEID key) {
		ValidatingDataEntry de = getOrderedEntries().get(key);
		if (de == null) {
			de = new ValidatingDataEntry(key) {
				@Override
				public boolean validate(final String s) {
					return PropvalEditor.this.validate(s);
				}
			};
			de.tf.addFocusListener(new FocusAdapter() {

				@Override
				public void focusLost(final FocusEvent e) {
					PropvalEditor.this.save();
				}
			});
			getOrderedEntries().put(key, de);
		}
		return de;
	}

	/**
	 * Set key supplier.
	 * 
	 * @param keySupplier
	 *            Key supplier.
	 */
	public void setKeySupplier(final KeySupplier keySupplier) {
		keysplr = keySupplier;
	}

	/**
	 * @return Panel.
	 */
	public TPanel getPanel() {
		if (panel == null) {
			panel = new TPanel();
			panel.setLayout(new BorderLayout());
			panel.add(BorderLayout.NORTH, new TLabel(getFileName()));
			panel.add(BorderLayout.CENTER, new JScrollPane(getPropsPanel()));
		}
		return panel;
	}

	/**
	 * Key supplier.
	 */
	private KeySupplier keysplr;
	/**
	 * Props Panel.
	 */
	private TPanel propsPanel;

	/**
	 * @return Props panel.
	 */
	private TPanel getPropsPanel() {
		if (propsPanel == null) {
			propsPanel = new TPanel(new GridBagLayout());
		}
		return propsPanel;
	}

	/**
	 * Panel.
	 */
	private TPanel panel;

	/**
	 * @param s
	 *            String to validate.
	 * @return True iff valid.
	 */
	protected abstract boolean validate(String s);
}
